package netmorph.optimization;

import model.optimization.PartOptimizer;
import static optimization.LinearAlgebra.*;

import common.notification.EmailNotifier;
import model.AbstractModel;
import optimization.OldLogger;
import optimization.NewtonNonlinearSolver;
 

public class ElongPartOptimizer extends PartOptimizer {

	public String getName() {
		return "Elong/PO";
	}

	double[] ysum;
	double sumN = 0;
	int state = 0; // FIXME ENUM
	int nSamples = 50;
	
	double[] basex;
	double[] h;
	double[] yph;
	double[] ymh;
	
	@Override
	public void init() {
		super.init();
		ysum = new double[modelPart.y.length];
		state = 0;
	
		basex = modelPart.x.clone();
		h = vMul(1/100.0, basex);
		modelPart.x = vAdd(basex, h);
	}
	
	@Override
	public void modelComputed(AbstractModel model) {
		super.modelComputed(null);

		// optimization step 
		ysum = vAdd(ysum, modelPart.y);
		sumN++;
		
		if (sumN >= nSamples) {
			if (state == 0) {
				yph = vMul(1.0/sumN, ysum);
				modelPart.x = vSub(basex, h);
				error = vNorm2(vSub(yph, modelPart.yt)); // not entirely true
				state = 1;
			} else {
				ymh = vMul(1.0/sumN, ysum);
				try {
					// Solve Jp=-r
					double[] jacob = vOuterMul(vSub(yph,ymh), vRecip(h));
					double[] p = NewtonNonlinearSolver.solveJPR(jacob, modelPart.y, modelPart.yt);
					// xn = x + dx
					double[] nx = vAdd(modelPart.x, p);
					OldLogger.log(name + " " + vPrint(modelPart.x, false) + " " + vPrint(nx, false) + " " + error);
					basex = nx;
					EmailNotifier.notifyNoFail(OldLogger.getLog());
				} catch (Exception e) {
					OldLogger.log(name + " " + vPrint(modelPart.x, false) + " " + "*** UPDATE FAILED *** " + " " + error + ";; " + e.getMessage());
					EmailNotifier.notifyNoFail(OldLogger.getLog());
					nSamples *= 1.5;
				}
				modelPart.x = vAdd(basex, h);
				state = 0;
			}
			ysum = new double[modelPart.y.length];
			sumN = 0;
		}
	}

}
